/*-------------------------------------------------------------------------
 *
 * queryjumble.c
 *	 查询规范化和指纹识别。
 *
 * 规范化是一个过程，其中类似的查询，通常只在其常量上有所不同
 * （尽管确切的规则要微妙得多），被视为等效，并被跟踪为
 * 单个条目。这对于非准备查询特别有用。
 *
 * 规范化是通过对查询进行指纹识别来实现的，选择性地
 * 序列化每个查询树节点中被认为对查询至关重要的字段。
 * 这被称为查询混乱。它与常规序列化不同，因为各种多余
 * 信息被忽略，被视为无关或对查询不重要，例如变量的排序和
 * 常量的值。
 *
 * 这个混乱在每个查询的解析分析结束时获取，并且
 * 其64位哈希被存储到查询的Query.queryId字段中。
 * 服务器随后在从查询生成的计划树中复制此值，使其可用。
 * 执行器可以使用此值将查询成本归咎于适当的queryId。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, 加利福尼亚大学董事会
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/misc/queryjumble.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "common/hashfn.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/queryjumble.h"

#define JUMBLE_SIZE				1024	/* 查询序列化缓冲区大小 */

/* GUC 参数 */
int			compute_query_id = COMPUTE_QUERY_ID_AUTO;

/* 当 compute_query_id 开启，或设为 AUTO 且某个模块请求它们时为真 */
bool		query_id_enabled = false;

static uint64 fc_compute_utility_query_id(const char *fc_str, int fc_query_location, int fc_query_len);
static void fc_AppendJumble(JumbleState *fc_jstate,
						 const unsigned char *fc_item, Size fc_size);
static void fc_JumbleQueryInternal(JumbleState *fc_jstate, Query *fc_query);
static void fc_JumbleRangeTable(JumbleState *fc_jstate, List *fc_rtable);
static void fc_JumbleRowMarks(JumbleState *fc_jstate, List *fc_rowMarks);
static void fc_JumbleExpr(JumbleState *fc_jstate, Node *fc_node);
static void fc_RecordConstLocation(JumbleState *fc_jstate, int fc_location);

/*
 * 在给定的可能包含多个语句的源字符串中，限制关注字符串的相关部分。
 */
const char * CleanQuerytext(const char *fc_query, int *fc_location, int *fc_len)
{
	int			fc_query_location = *fc_location;
	int			fc_query_len = *fc_len;

	/* 首先应用起始偏移量，除非它是 -1（未知）。 */
	if (fc_query_location >= 0)
	{
		Assert(fc_query_location <= strlen(fc_query));
		fc_query += fc_query_location;
		/* 长度为 0（或 -1）意味着“字符串的其余部分” */
		if (fc_query_len <= 0)
			fc_query_len = strlen(fc_query);
		else
			Assert(fc_query_len <= strlen(fc_query));
	}
	else
	{
		/* 如果查询位置未知，也要不信任 query_len */
		fc_query_location = 0;
		fc_query_len = strlen(fc_query);
	}

	/*
	 * 也丢弃前导和尾部空格。使用 scanner_isspace()
	 * 而非 libc 的 isspace()，因为我们希望匹配词法分析器的行为。
	 */
	while (fc_query_len > 0 && scanner_isspace(fc_query[0]))
		fc_query++, fc_query_location++, fc_query_len--;
	while (fc_query_len > 0 && scanner_isspace(fc_query[fc_query_len - 1]))
		fc_query_len--;

	*fc_location = fc_query_location;
	*fc_len = fc_query_len;

	return fc_query;
}

JumbleState *
JumbleQuery(Query *fc_query, const char *fc_querytext)
{
	JumbleState *fc_jstate = NULL;

	Assert(IsQueryIdEnabled());

	if (fc_query->utilityStmt)
	{
		fc_query->queryId = fc_compute_utility_query_id(fc_querytext,
												  fc_query->stmt_location,
												  fc_query->stmt_len);
	}
	else
	{
		fc_jstate = (JumbleState *) palloc(sizeof(JumbleState));

		/* 为查询混乱设置工作空间 */
		fc_jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
		fc_jstate->jumble_len = 0;
		fc_jstate->clocations_buf_size = 32;
		fc_jstate->clocations = (LocationLen *)
			palloc(fc_jstate->clocations_buf_size * sizeof(LocationLen));
		fc_jstate->clocations_count = 0;
		fc_jstate->highest_extern_param_id = 0;

		/* 计算查询 ID 并用其标记查询节点 */
		fc_JumbleQueryInternal(fc_jstate, fc_query);
		fc_query->queryId = DatumGetUInt64(hash_any_extended(fc_jstate->jumble,
														  fc_jstate->jumble_len,
														  0));

		/*
		 * 如果我们运气不佳得到了零哈希值，改用 1，以
		 * 防止与工具语句案例的混淆。
		 */
		if (fc_query->queryId == UINT64CONST(0))
			fc_query->queryId = UINT64CONST(1);
	}

	return fc_jstate;
}

/*
 * 启用查询标识符计算。
 *
 * 第三方插件可以使用此函数通知核心它们需要
 * 计算查询标识符。
 */
void EnableQueryId(void)
{
	if (compute_query_id != COMPUTE_QUERY_ID_OFF)
		query_id_enabled = true;
}

/*
 * 为给定的工具查询字符串计算查询标识符。
 */
static uint64 fc_compute_utility_query_id(const char *fc_query_text, int fc_query_location, int fc_query_len)
{
	uint64		fc_queryId;
	const char *fc_sql;

	/*
	 * 如果查询是多语句源字符串的一部分，则将注意力限制在字符串的相关部分。
	 */
	fc_sql = CleanQuerytext(fc_query_text, &fc_query_location, &fc_query_len);

	fc_queryId = DatumGetUInt64(hash_any_extended((const unsigned char *) fc_sql,
											   fc_query_len, 0));

	/*
	 * 如果我们运气不佳得到了零哈希值（无效），改用查询 ID 2，
	 * 查询 ID 1 已用于正常语句。
	 */
	if (fc_queryId == UINT64CONST(0))
		fc_queryId = UINT64CONST(2);

	return fc_queryId;
}

/*
 * AppendJumble：将给定查询中的一个实质值附加到
 * 当前混合中。
 */
static void fc_AppendJumble(JumbleState *fc_jstate, const unsigned char *fc_item, Size fc_size)
{
	unsigned char *fc_jumble = fc_jstate->jumble;
	Size		fc_jumble_len = fc_jstate->jumble_len;

	/*
	 * 每当混合缓冲区已满，我们会对当前内容进行哈希，
	 * 并重置缓冲区以仅包含该哈希值，从而依赖哈希来总结到目前为止的所有内容。
	 */
	while (fc_size > 0)
	{
		Size		fc_part_size;

		if (fc_jumble_len >= JUMBLE_SIZE)
		{
			uint64		fc_start_hash;

			fc_start_hash = DatumGetUInt64(hash_any_extended(fc_jumble,
														  JUMBLE_SIZE, 0));
			memcpy(fc_jumble, &fc_start_hash, sizeof(fc_start_hash));
			fc_jumble_len = sizeof(fc_start_hash);
		}
		fc_part_size = Min(fc_size, JUMBLE_SIZE - fc_jumble_len);
		memcpy(fc_jumble + fc_jumble_len, fc_item, fc_part_size);
		fc_jumble_len += fc_part_size;
		fc_item += fc_part_size;
		fc_size -= fc_part_size;
	}
	fc_jstate->jumble_len = fc_jumble_len;
}

/*
 * 对 AppendJumble 的包装，以封装
 * 单个局部变量元素的序列化细节。
 */
#define APP_JUMB(item) \
	fc_AppendJumble(fc_jstate, (const unsigned char *) &(item), sizeof(item))
#define APP_JUMB_STRING(str) \
	fc_AppendJumble(fc_jstate, (const unsigned char *) (str), strlen(str) + 1)

/*
 * JumbleQueryInternal：选择性序列化查询树，
 * 同时将重要数据附加到“查询混合”，忽略不重要的数据。
 *
 * 包含内容的经验法则是，我们应忽略任何不
 * 语义上重要的内容（例如别名）以及任何可以
 * 从子节点推断的信息（否则我们只是在重复哈希那部分
 * 信息）。
 */
static void fc_JumbleQueryInternal(JumbleState *fc_jstate, Query *fc_query)
{
	Assert(IsA(fc_query, Query));
	Assert(fc_query->utilityStmt == NULL);

	APP_JUMB(fc_query->commandType);
	/* resultRelation 通常可以从 commandType 预测 */
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->cteList);
	fc_JumbleRangeTable(fc_jstate, fc_query->rtable);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->jointree);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->mergeActionList);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->targetList);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->onConflict);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->returningList);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->groupClause);
	APP_JUMB(fc_query->groupDistinct);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->groupingSets);
	fc_JumbleExpr(fc_jstate, fc_query->havingQual);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->windowClause);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->distinctClause);
	fc_JumbleExpr(fc_jstate, (Node *) fc_query->sortClause);
	fc_JumbleExpr(fc_jstate, fc_query->limitOffset);
	fc_JumbleExpr(fc_jstate, fc_query->limitCount);
	APP_JUMB(fc_query->limitOption);
	fc_JumbleRowMarks(fc_jstate, fc_query->rowMarks);
	fc_JumbleExpr(fc_jstate, fc_query->setOperations);
}

/*
 * 混乱范围表
 */
static void fc_JumbleRangeTable(JumbleState *fc_jstate, List *fc_rtable)
{
	ListCell   *fc_lc;

	foreach(fc_lc, fc_rtable)
	{
		RangeTblEntry *fc_rte = lfirst_node(RangeTblEntry, fc_lc);

		APP_JUMB(fc_rte->rtekind);
		switch (fc_rte->rtekind)
		{
			case RTE_RELATION:
				APP_JUMB(fc_rte->relid);
				fc_JumbleExpr(fc_jstate, (Node *) fc_rte->tablesample);
				APP_JUMB(fc_rte->inh);
				break;
			case RTE_SUBQUERY:
				fc_JumbleQueryInternal(fc_jstate, fc_rte->subquery);
				break;
			case RTE_JOIN:
				APP_JUMB(fc_rte->jointype);
				break;
			case RTE_FUNCTION:
				fc_JumbleExpr(fc_jstate, (Node *) fc_rte->functions);
				break;
			case RTE_TABLEFUNC:
				fc_JumbleExpr(fc_jstate, (Node *) fc_rte->tablefunc);
				break;
			case RTE_VALUES:
				fc_JumbleExpr(fc_jstate, (Node *) fc_rte->values_lists);
				break;
			case RTE_CTE:

				/*
				 * 这里依赖 CTE 名称并不是理想的，但这是我们
				 * 唯一可以用来识别引用的 WITH 项目的信息。
				 */
				APP_JUMB_STRING(fc_rte->ctename);
				APP_JUMB(fc_rte->ctelevelsup);
				break;
			case RTE_NAMEDTUPLESTORE:
				APP_JUMB_STRING(fc_rte->enrname);
				break;
			case RTE_RESULT:
				break;
			default:
				elog(ERROR, "unrecognized RTE kind: %d", (int) fc_rte->rtekind);
				break;
		}
	}
}

/*
 * 混乱 rowMarks 列表
 */
static void fc_JumbleRowMarks(JumbleState *fc_jstate, List *fc_rowMarks)
{
	ListCell   *fc_lc;

	foreach(fc_lc, fc_rowMarks)
	{
		RowMarkClause *fc_rowmark = lfirst_node(RowMarkClause, fc_lc);

		if (!fc_rowmark->pushedDown)
		{
			APP_JUMB(fc_rowmark->rti);
			APP_JUMB(fc_rowmark->strength);
			APP_JUMB(fc_rowmark->waitPolicy);
		}
	}
}

/*
 * 打乱表达式树
 *
 * 一般来说，这个函数应处理与expression_tree_walker()相同的所有节点类型，因此它的编码尽可能与该函数平行。然而，既然我们仅在解析分析后立即被调用，我们不需要处理只在规划中出现的节点类型。
 *
 * 注意：我们不直接使用expression_tree_walker()的原因是，该函数的目的是支持不关心大多数树节点类型的树遍历器，但在这里我们关心所有类型。对于任何未识别的节点类型，我们应提出警告。
 */
static void fc_JumbleExpr(JumbleState *fc_jstate, Node *fc_node)
{
	ListCell   *fc_temp;

	if (fc_node == NULL)
		return;

	/* 防止因过于复杂的表达式导致栈溢出 */
	check_stack_depth();

	/*
	 * 我们总是输出节点的NodeTag，然后是任何被视为重要的附加字段，然后我们递归到任何子节点。
	 */
	APP_JUMB(fc_node->type);

	switch (nodeTag(fc_node))
	{
		case T_Var:
			{
				Var		   *fc_var = (Var *) fc_node;

				APP_JUMB(fc_var->varno);
				APP_JUMB(fc_var->varattno);
				APP_JUMB(fc_var->varlevelsup);
			}
			break;
		case T_Const:
			{
				Const	   *fc_c = (Const *) fc_node;

				/* 我们只打乱常量的类型，而不是它的值 */
				APP_JUMB(fc_c->consttype);
				/* 此外，记录其解析位置以进行查询规范化 */
				fc_RecordConstLocation(fc_jstate, fc_c->location);
			}
			break;
		case T_Param:
			{
				Param	   *fc_p = (Param *) fc_node;

				APP_JUMB(fc_p->paramkind);
				APP_JUMB(fc_p->paramid);
				APP_JUMB(fc_p->paramtype);
				/* 此外，跟踪最高的外部Param id */
				if (fc_p->paramkind == PARAM_EXTERN &&
					fc_p->paramid > fc_jstate->highest_extern_param_id)
					fc_jstate->highest_extern_param_id = fc_p->paramid;
			}
			break;
		case T_Aggref:
			{
				Aggref	   *fc_expr = (Aggref *) fc_node;

				APP_JUMB(fc_expr->aggfnoid);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->aggdirectargs);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->args);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->aggorder);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->aggdistinct);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->aggfilter);
			}
			break;
		case T_GroupingFunc:
			{
				GroupingFunc *fc_grpnode = (GroupingFunc *) fc_node;

				fc_JumbleExpr(fc_jstate, (Node *) fc_grpnode->refs);
				APP_JUMB(fc_grpnode->agglevelsup);
			}
			break;
		case T_WindowFunc:
			{
				WindowFunc *fc_expr = (WindowFunc *) fc_node;

				APP_JUMB(fc_expr->winfnoid);
				APP_JUMB(fc_expr->winref);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->args);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->aggfilter);
			}
			break;
		case T_SubscriptingRef:
			{
				SubscriptingRef *fc_sbsref = (SubscriptingRef *) fc_node;

				fc_JumbleExpr(fc_jstate, (Node *) fc_sbsref->refupperindexpr);
				fc_JumbleExpr(fc_jstate, (Node *) fc_sbsref->reflowerindexpr);
				fc_JumbleExpr(fc_jstate, (Node *) fc_sbsref->refexpr);
				fc_JumbleExpr(fc_jstate, (Node *) fc_sbsref->refassgnexpr);
			}
			break;
		case T_FuncExpr:
			{
				FuncExpr   *fc_expr = (FuncExpr *) fc_node;

				APP_JUMB(fc_expr->funcid);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->args);
			}
			break;
		case T_NamedArgExpr:
			{
				NamedArgExpr *fc_nae = (NamedArgExpr *) fc_node;

				APP_JUMB(fc_nae->argnumber);
				fc_JumbleExpr(fc_jstate, (Node *) fc_nae->arg);
			}
			break;
		case T_OpExpr:
		case T_DistinctExpr:	/* 相当于OpExpr的结构 */
		case T_NullIfExpr:		/* 相当于OpExpr的结构 */
			{
				OpExpr	   *fc_expr = (OpExpr *) fc_node;

				APP_JUMB(fc_expr->opno);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->args);
			}
			break;
		case T_ScalarArrayOpExpr:
			{
				ScalarArrayOpExpr *fc_expr = (ScalarArrayOpExpr *) fc_node;

				APP_JUMB(fc_expr->opno);
				APP_JUMB(fc_expr->useOr);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->args);
			}
			break;
		case T_BoolExpr:
			{
				BoolExpr   *fc_expr = (BoolExpr *) fc_node;

				APP_JUMB(fc_expr->boolop);
				fc_JumbleExpr(fc_jstate, (Node *) fc_expr->args);
			}
			break;
		case T_SubLink:
			{
				SubLink    *fc_sublink = (SubLink *) fc_node;

				APP_JUMB(fc_sublink->subLinkType);
				APP_JUMB(fc_sublink->subLinkId);
				fc_JumbleExpr(fc_jstate, (Node *) fc_sublink->testexpr);
				fc_JumbleQueryInternal(fc_jstate, castNode(Query, fc_sublink->subselect));
			}
			break;
		case T_FieldSelect:
			{
				FieldSelect *fc_fs = (FieldSelect *) fc_node;

				APP_JUMB(fc_fs->fieldnum);
				fc_JumbleExpr(fc_jstate, (Node *) fc_fs->arg);
			}
			break;
		case T_FieldStore:
			{
				FieldStore *fc_fstore = (FieldStore *) fc_node;

				fc_JumbleExpr(fc_jstate, (Node *) fc_fstore->arg);
				fc_JumbleExpr(fc_jstate, (Node *) fc_fstore->newvals);
			}
			break;
		case T_RelabelType:
			{
				RelabelType *fc_rt = (RelabelType *) fc_node;

				APP_JUMB(fc_rt->resulttype);
				fc_JumbleExpr(fc_jstate, (Node *) fc_rt->arg);
			}
			break;
		case T_CoerceViaIO:
			{
				CoerceViaIO *fc_cio = (CoerceViaIO *) fc_node;

				APP_JUMB(fc_cio->resulttype);
				fc_JumbleExpr(fc_jstate, (Node *) fc_cio->arg);
			}
			break;
		case T_ArrayCoerceExpr:
			{
				ArrayCoerceExpr *fc_acexpr = (ArrayCoerceExpr *) fc_node;

				APP_JUMB(fc_acexpr->resulttype);
				fc_JumbleExpr(fc_jstate, (Node *) fc_acexpr->arg);
				fc_JumbleExpr(fc_jstate, (Node *) fc_acexpr->elemexpr);
			}
			break;
		case T_ConvertRowtypeExpr:
			{
				ConvertRowtypeExpr *fc_crexpr = (ConvertRowtypeExpr *) fc_node;

				APP_JUMB(fc_crexpr->resulttype);
				fc_JumbleExpr(fc_jstate, (Node *) fc_crexpr->arg);
			}
			break;
		case T_CollateExpr:
			{
				CollateExpr *fc_ce = (CollateExpr *) fc_node;

				APP_JUMB(fc_ce->collOid);
				fc_JumbleExpr(fc_jstate, (Node *) fc_ce->arg);
			}
			break;
		case T_CaseExpr:
			{
				CaseExpr   *fc_caseexpr = (CaseExpr *) fc_node;

				fc_JumbleExpr(fc_jstate, (Node *) fc_caseexpr->arg);
				foreach(fc_temp, fc_caseexpr->args)
				{
					CaseWhen   *fc_when = lfirst_node(CaseWhen, fc_temp);

					fc_JumbleExpr(fc_jstate, (Node *) fc_when->expr);
					fc_JumbleExpr(fc_jstate, (Node *) fc_when->result);
				}
				fc_JumbleExpr(fc_jstate, (Node *) fc_caseexpr->defresult);
			}
			break;
		case T_CaseTestExpr:
			{
				CaseTestExpr *fc_ct = (CaseTestExpr *) fc_node;

				APP_JUMB(fc_ct->typeId);
			}
			break;
		case T_ArrayExpr:
			fc_JumbleExpr(fc_jstate, (Node *) ((ArrayExpr *) fc_node)->elements);
			break;
		case T_RowExpr:
			fc_JumbleExpr(fc_jstate, (Node *) ((RowExpr *) fc_node)->args);
			break;
		case T_RowCompareExpr:
			{
				RowCompareExpr *fc_rcexpr = (RowCompareExpr *) fc_node;

				APP_JUMB(fc_rcexpr->rctype);
				fc_JumbleExpr(fc_jstate, (Node *) fc_rcexpr->largs);
				fc_JumbleExpr(fc_jstate, (Node *) fc_rcexpr->rargs);
			}
			break;
		case T_CoalesceExpr:
			fc_JumbleExpr(fc_jstate, (Node *) ((CoalesceExpr *) fc_node)->args);
			break;
		case T_MinMaxExpr:
			{
				MinMaxExpr *fc_mmexpr = (MinMaxExpr *) fc_node;

				APP_JUMB(fc_mmexpr->op);
				fc_JumbleExpr(fc_jstate, (Node *) fc_mmexpr->args);
			}
			break;
		case T_SQLValueFunction:
			{
				SQLValueFunction *fc_svf = (SQLValueFunction *) fc_node;

				APP_JUMB(fc_svf->op);
				/* 类型完全由op决定 */
				APP_JUMB(fc_svf->typmod);
			}
			break;
		case T_XmlExpr:
			{
				XmlExpr    *fc_xexpr = (XmlExpr *) fc_node;

				APP_JUMB(fc_xexpr->op);
				fc_JumbleExpr(fc_jstate, (Node *) fc_xexpr->named_args);
				fc_JumbleExpr(fc_jstate, (Node *) fc_xexpr->args);
			}
			break;
		case T_NullTest:
			{
				NullTest   *fc_nt = (NullTest *) fc_node;

				APP_JUMB(fc_nt->nulltesttype);
				fc_JumbleExpr(fc_jstate, (Node *) fc_nt->arg);
			}
			break;
		case T_BooleanTest:
			{
				BooleanTest *fc_bt = (BooleanTest *) fc_node;

				APP_JUMB(fc_bt->booltesttype);
				fc_JumbleExpr(fc_jstate, (Node *) fc_bt->arg);
			}
			break;
		case T_CoerceToDomain:
			{
				CoerceToDomain *fc_cd = (CoerceToDomain *) fc_node;

				APP_JUMB(fc_cd->resulttype);
				fc_JumbleExpr(fc_jstate, (Node *) fc_cd->arg);
			}
			break;
		case T_CoerceToDomainValue:
			{
				CoerceToDomainValue *fc_cdv = (CoerceToDomainValue *) fc_node;

				APP_JUMB(fc_cdv->typeId);
			}
			break;
		case T_SetToDefault:
			{
				SetToDefault *fc_sd = (SetToDefault *) fc_node;

				APP_JUMB(fc_sd->typeId);
			}
			break;
		case T_CurrentOfExpr:
			{
				CurrentOfExpr *fc_ce = (CurrentOfExpr *) fc_node;

				APP_JUMB(fc_ce->cvarno);
				if (fc_ce->cursor_name)
					APP_JUMB_STRING(fc_ce->cursor_name);
				APP_JUMB(fc_ce->cursor_param);
			}
			break;
		case T_NextValueExpr:
			{
				NextValueExpr *fc_nve = (NextValueExpr *) fc_node;

				APP_JUMB(fc_nve->seqid);
				APP_JUMB(fc_nve->typeId);
			}
			break;
		case T_InferenceElem:
			{
				InferenceElem *fc_ie = (InferenceElem *) fc_node;

				APP_JUMB(fc_ie->infercollid);
				APP_JUMB(fc_ie->inferopclass);
				fc_JumbleExpr(fc_jstate, fc_ie->expr);
			}
			break;
		case T_TargetEntry:
			{
				TargetEntry *fc_tle = (TargetEntry *) fc_node;

				APP_JUMB(fc_tle->resno);
				APP_JUMB(fc_tle->ressortgroupref);
				fc_JumbleExpr(fc_jstate, (Node *) fc_tle->expr);
			}
			break;
		case T_RangeTblRef:
			{
				RangeTblRef *fc_rtr = (RangeTblRef *) fc_node;

				APP_JUMB(fc_rtr->rtindex);
			}
			break;
		case T_JoinExpr:
			{
				JoinExpr   *fc_join = (JoinExpr *) fc_node;

				APP_JUMB(fc_join->jointype);
				APP_JUMB(fc_join->isNatural);
				APP_JUMB(fc_join->rtindex);
				fc_JumbleExpr(fc_jstate, fc_join->larg);
				fc_JumbleExpr(fc_jstate, fc_join->rarg);
				fc_JumbleExpr(fc_jstate, fc_join->quals);
			}
			break;
		case T_FromExpr:
			{
				FromExpr   *fc_from = (FromExpr *) fc_node;

				fc_JumbleExpr(fc_jstate, (Node *) fc_from->fromlist);
				fc_JumbleExpr(fc_jstate, fc_from->quals);
			}
			break;
		case T_OnConflictExpr:
			{
				OnConflictExpr *fc_conf = (OnConflictExpr *) fc_node;

				APP_JUMB(fc_conf->action);
				fc_JumbleExpr(fc_jstate, (Node *) fc_conf->arbiterElems);
				fc_JumbleExpr(fc_jstate, fc_conf->arbiterWhere);
				fc_JumbleExpr(fc_jstate, (Node *) fc_conf->onConflictSet);
				fc_JumbleExpr(fc_jstate, fc_conf->onConflictWhere);
				APP_JUMB(fc_conf->constraint);
				APP_JUMB(fc_conf->exclRelIndex);
				fc_JumbleExpr(fc_jstate, (Node *) fc_conf->exclRelTlist);
			}
			break;
		case T_MergeAction:
			{
				MergeAction *fc_mergeaction = (MergeAction *) fc_node;

				APP_JUMB(fc_mergeaction->matched);
				APP_JUMB(fc_mergeaction->commandType);
				fc_JumbleExpr(fc_jstate, fc_mergeaction->qual);
				fc_JumbleExpr(fc_jstate, (Node *) fc_mergeaction->targetList);
			}
			break;
		case T_List:
			foreach(fc_temp, (List *) fc_node)
			{
				fc_JumbleExpr(fc_jstate, (Node *) lfirst(fc_temp));
			}
			break;
		case T_IntList:
			foreach(fc_temp, (List *) fc_node)
			{
				APP_JUMB(lfirst_int(fc_temp));
			}
			break;
		case T_SortGroupClause:
			{
				SortGroupClause *fc_sgc = (SortGroupClause *) fc_node;

				APP_JUMB(fc_sgc->tleSortGroupRef);
				APP_JUMB(fc_sgc->eqop);
				APP_JUMB(fc_sgc->sortop);
				APP_JUMB(fc_sgc->nulls_first);
			}
			break;
		case T_GroupingSet:
			{
				GroupingSet *fc_gsnode = (GroupingSet *) fc_node;

				fc_JumbleExpr(fc_jstate, (Node *) fc_gsnode->content);
			}
			break;
		case T_WindowClause:
			{
				WindowClause *fc_wc = (WindowClause *) fc_node;

				APP_JUMB(fc_wc->winref);
				APP_JUMB(fc_wc->frameOptions);
				fc_JumbleExpr(fc_jstate, (Node *) fc_wc->partitionClause);
				fc_JumbleExpr(fc_jstate, (Node *) fc_wc->orderClause);
				fc_JumbleExpr(fc_jstate, fc_wc->startOffset);
				fc_JumbleExpr(fc_jstate, fc_wc->endOffset);
			}
			break;
		case T_CommonTableExpr:
			{
				CommonTableExpr *fc_cte = (CommonTableExpr *) fc_node;

				/* 我们存储字符串名称，因为RTE_CTE RTEs需要它 */
				APP_JUMB_STRING(fc_cte->ctename);
				APP_JUMB(fc_cte->ctematerialized);
				fc_JumbleQueryInternal(fc_jstate, castNode(Query, fc_cte->ctequery));
			}
			break;
		case T_SetOperationStmt:
			{
				SetOperationStmt *fc_setop = (SetOperationStmt *) fc_node;

				APP_JUMB(fc_setop->op);
				APP_JUMB(fc_setop->all);
				fc_JumbleExpr(fc_jstate, fc_setop->larg);
				fc_JumbleExpr(fc_jstate, fc_setop->rarg);
			}
			break;
		case T_RangeTblFunction:
			{
				RangeTblFunction *fc_rtfunc = (RangeTblFunction *) fc_node;

				fc_JumbleExpr(fc_jstate, fc_rtfunc->funcexpr);
			}
			break;
		case T_TableFunc:
			{
				TableFunc  *fc_tablefunc = (TableFunc *) fc_node;

				fc_JumbleExpr(fc_jstate, fc_tablefunc->docexpr);
				fc_JumbleExpr(fc_jstate, fc_tablefunc->rowexpr);
				fc_JumbleExpr(fc_jstate, (Node *) fc_tablefunc->colexprs);
			}
			break;
		case T_TableSampleClause:
			{
				TableSampleClause *fc_tsc = (TableSampleClause *) fc_node;

				APP_JUMB(fc_tsc->tsmhandler);
				fc_JumbleExpr(fc_jstate, (Node *) fc_tsc->args);
				fc_JumbleExpr(fc_jstate, (Node *) fc_tsc->repeatable);
			}
			break;
		default:
			/* 这只是一个警告，因为我们仍然可以继续处理 */
			elog(WARNING, "unrecognized node type: %d",
				 (int) nodeTag(fc_node));
			break;
	}
}

/*
 * 记录当前遍历的查询树中常量在查询字符串中的位置。
 */
static void fc_RecordConstLocation(JumbleState *fc_jstate, int fc_location)
{
	/* -1表示未知或未定义位置 */
	if (fc_location >= 0)
	{
		/* 如有需要则扩大数组 */
		if (fc_jstate->clocations_count >= fc_jstate->clocations_buf_size)
		{
			fc_jstate->clocations_buf_size *= 2;
			fc_jstate->clocations = (LocationLen *)
				repalloc(fc_jstate->clocations,
						 fc_jstate->clocations_buf_size *
						 sizeof(LocationLen));
		}
		fc_jstate->clocations[fc_jstate->clocations_count].location = fc_location;
		/* 将长度初始化为-1以简化第三方模块的使用 */
		fc_jstate->clocations[fc_jstate->clocations_count].length = -1;
		fc_jstate->clocations_count++;
	}
}
